﻿using EasyNet.Componets.Core.Logger;
using EasyNet.Componets.Core.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace EasyNet.Componets.Core.Serialization
{
    public class EasyNetXmlSerialization : IXmlSerialization
    {
        #region 序列化
        public string Serialize(object obj)
        {
            return Serialize(obj, "xml");
        }

        public string Serialize(object obj, string rootNodeName)
        {
            if (obj == null) return string.Empty;
            XmlDocument xmldoc = new XmlDocument();
            XmlElement xmlElement = xmldoc.CreateElement(rootNodeName);

            xmldoc.AppendChild(xmlElement);
            Serialize(xmldoc, xmlElement, obj);

            return xmldoc.OuterXml;
        }

        private void Serialize(XmlDocument xmldoc, XmlElement rootElemnt, object obj)
        {
            if (obj == null) return;

            foreach (PropertyInfo property in obj.GetType().GetProperties())
            {
                Serialize(xmldoc, rootElemnt, obj, property);
                
              
            }
        }

        private void Serialize(XmlDocument xmldoc, XmlElement rootElemnt, object obj, PropertyInfo property)
        {
            XmlElement attribute = xmldoc.CreateElement(property.Name);

            try
            {
                var propertyValue = property.GetValue(obj, null);
                if (property == null)
                {
                    attribute.InnerText = string.Empty;
                    rootElemnt.AppendChild(attribute);
                    return ;
                }

                if (property.PropertyType.IsValueType
                 || property.PropertyType == typeof(string)
                 || property.PropertyType == typeof(System.Guid))
                {
                    attribute.InnerText = propertyValue.ToString();
                }
                else if (property.PropertyType.IsGenericType
                     && property.PropertyType.GetGenericTypeDefinition() == typeof(IList<>))
                {
                    var list = propertyValue as IList;
                    foreach(var item in list)
                    {
                        var child = xmldoc.CreateElement(property.Name);
                        Serialize(xmldoc, child, item);
                        rootElemnt.AppendChild(child);
                    }
                    return;
                }else if(property.PropertyType.IsArray)
                {
                    var list = propertyValue as Array;
                    foreach (var item in list)
                    {
                        var child = xmldoc.CreateElement(property.Name);
                        Serialize(xmldoc, child, item);
                        rootElemnt.AppendChild(child);
                    }
                    return;
                }
                else if (property.PropertyType.IsGenericType
                    || property.PropertyType.IsClass)
                {
                    Serialize(xmldoc, attribute, propertyValue);
                }
                else
                {
                    attribute.InnerText = propertyValue.ToString();
                }
            }catch(Exception ex)
            {
                attribute.InnerText = string.Empty;
                string msg = string.Format("EasyNetXmlSerialization 序列化异常！属性名称：{0}\r\n对象：{1}", property.Name, SerializationUtil.Json.Serialize(obj));
                LogUtil<EasyNetXmlSerialization>.LogError(msg, ex);
            }
            rootElemnt.AppendChild(attribute);
        }
        #endregion

        #region 反序列化
        public T Deserialize<T>(string value) where T : new()
        {
            if (string.IsNullOrEmpty(value)) return default(T);
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.LoadXml(value);

            var root = xmldoc.FirstChild;
            if(root == null) return default(T);

            var model = new T();
            DeserializeFromXml(model, root);

            return model;
        }

        public T Deserialize<T>(string value, string rootNodeName) where T : new()
        {
            if (string.IsNullOrEmpty(value)) return default(T);
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.LoadXml(value);

            var root = xmldoc.SelectSingleNode(rootNodeName);
            if (root == null) return default(T);

            var model = new T();
            DeserializeFromXml(model, root);

            return model;
        }

        private void DeserializeFromXml(object model, XmlNode rootNode)
        {
            XmlNodeList attributes = rootNode.ChildNodes;
            if (attributes == null || attributes.Count == 0)
            {
                return;
            }
            var propertyArray = model.GetType().GetProperties();
            foreach (XmlNode node in attributes)
            {
                foreach (PropertyInfo property in propertyArray)
                {
                    if (node.Name.ToLower() == property.Name.ToLower())
                    {
                        DeserializeFromXml(model, node, property);
                        break;
                    }

                }

            }

        }

        private void DeserializeFromXml(object model, XmlNode node, PropertyInfo property)
        {
            try
            {
                if (string.IsNullOrEmpty(node.InnerText) && string.IsNullOrEmpty(node.InnerXml))
                {
                    return;
                }
                if (property.PropertyType.IsValueType
                    || property.PropertyType == typeof(string))
                {
                    property.SetValue(model, Convert.ChangeType(node.InnerText, property.PropertyType), null);
                }
                else if (property.PropertyType.IsGenericType
                    && property.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
                {
                    var childNodeList = node.ChildNodes;
                    if (childNodeList != null && childNodeList.Count > 0)
                    {
                        //获取泛型类型类型
                        var nestedType = property.PropertyType.GenericTypeArguments.FirstOrDefault();
                        var list = Activator.CreateInstance(property.PropertyType) as IList;

                        foreach (XmlNode childNode in childNodeList)
                        {
                            if (nestedType != null)
                            {
                                var nestedModel = Activator.CreateInstance(nestedType);
                                DeserializeFromXml(nestedModel, childNode);
                                list.Add(nestedModel);
                            }
                        }

                        property.SetValue(model, list, null);
                    }
                }
                else if (property.PropertyType == typeof(System.Guid))
                {
                    property.SetValue(model, new Guid(node.InnerText), null);
                }
                else if (property.PropertyType.IsGenericType
                    || property.PropertyType.IsClass)
                {
                    var type = property.PropertyType;
                    var propertyModel = Activator.CreateInstance(type);
                    DeserializeFromXml(propertyModel, node);
                    property.SetValue(model, propertyModel, null);
                }
                else
                {
                    property.SetValue(model, Convert.ChangeType(node.InnerText, property.PropertyType), null);
                }
            }
            catch (System.Exception ex)
            {
                string msg = string.Format("EasyNetXmlSerialization 反序列化异常！属性名称：{0} XmlNodeInnerText：{1}", property.Name, node.InnerText);
                LogUtil<EasyNetXmlSerialization>.LogError(msg, ex);
            }

        }
        #endregion


    }
}
